package cmd

import (
	"fmt"
	"log/syslog"

	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	lsyslog "github.com/sirupsen/logrus/hooks/syslog"
	"google.golang.org/grpc/resolver"

	"network/v3/internal/adr"
	"network/v3/internal/api/ns"
	"network/v3/internal/backend/applicationserver"
	"network/v3/internal/backend/controller"
	gwbackend "network/v3/internal/backend/gateway"
	"network/v3/internal/backend/gateway/mqtt"
	"network/v3/internal/backend/joinserver"
	"network/v3/internal/band"
	"network/v3/internal/config"
	"network/v3/internal/downlink"
	"network/v3/internal/gateway"
	"network/v3/internal/storage"
	"network/v3/internal/uplink"
	log "network/v3/pkg/logger"
)

func setGRPCResolver() error {
	resolver.SetDefaultScheme(config.C.General.GRPCDefaultResolverScheme)
	return nil
}

func setupBand() error {
	if err := band.Setup(config.C); err != nil {
		return errors.Wrap(err, "setup band error")
	}
	log.Info("lora band is Setup, named:", band.Band().Name())

	return nil
}

func setRXParameters() error {
	defaults := band.Band().GetDefaults()

	networkSettings := config.C.NetworkServer.NetworkSettings
	if networkSettings.RX2DR == -1 {
		config.C.NetworkServer.NetworkSettings.RX2DR = defaults.RX2DataRate
	}

	if networkSettings.RX2Frequency == -1 {
		config.C.NetworkServer.NetworkSettings.RX2Frequency = defaults.RX2Frequency
	}

	return nil
}

func setLogLevel() error {
	log.SetLevel(log.Level(uint8(config.C.General.LogLevel)))
	return nil
}

func setSyslog() error {
	if !config.C.General.LogToSyslog {
		return nil
	}

	var prio syslog.Priority

	switch logrus.StandardLogger().Level {
	case logrus.DebugLevel:
		prio = syslog.LOG_USER | syslog.LOG_DEBUG
	case logrus.InfoLevel:
		prio = syslog.LOG_USER | syslog.LOG_INFO
	case logrus.WarnLevel:
		prio = syslog.LOG_USER | syslog.LOG_WARNING
	case logrus.ErrorLevel:
		prio = syslog.LOG_USER | syslog.LOG_ERR
	case logrus.FatalLevel:
		prio = syslog.LOG_USER | syslog.LOG_CRIT
	case logrus.PanicLevel:
		prio = syslog.LOG_USER | syslog.LOG_CRIT
	}

	hook, err := lsyslog.NewSyslogHook("", "", prio, "chirpstack-network-server")
	if err != nil {
		return errors.Wrap(err, "get syslog hook error")
	}

	logrus.AddHook(hook)

	return nil
}

func printStartMessage() error {
	networkServer := config.C.NetworkServer
	log.WithFields(log.Fields{
		"version": version,
		"net_id":  networkServer.NetID.String(),
		"band":    networkServer.Band.Name,
		"docs":    "https://www.chirpstack.io/",
	}).Info("starting ChirpStack Network Server")
	return nil
}

func enableUplinkChannels() error {
	if len(config.C.NetworkServer.NetworkSettings.EnabledUplinkChannels) == 0 {
		return nil
	}

	log.Info("disabling all channels")
	for _, disableUplinkChannel := range band.Band().GetEnabledUplinkChannelIndices() {
		if err := band.Band().DisableUplinkChannelIndex(disableUplinkChannel); err != nil {
			return errors.Wrap(err, "disable uplink channel error")
		}
	}

	log.WithField("channels", config.C.NetworkServer.NetworkSettings.EnabledUplinkChannels).Info("enabling channels")
	for _, enabledUplinkChannel := range config.C.NetworkServer.NetworkSettings.EnabledUplinkChannels {
		if err := band.Band().EnableUplinkChannelIndex(enabledUplinkChannel); err != nil {
			return errors.Wrap(err, "enable uplink channel error")
		}
	}

	return nil
}

func setupStorage() error {
	if err := storage.Setup(config.C); err != nil {
		return errors.Wrap(err, "setup storage error")
	}
	return nil
}

func setupADR() error {
	if err := adr.Setup(config.C); err != nil {
		return errors.Wrap(err, "setup adr error")
	}
	return nil
}

func setGatewayBackend() error {
	var (
		err error
		gw  gwbackend.Gateway
	)

	switch config.C.NetworkServer.Gateway.Backend.Type {
	case "mqtt":
		gw, err = mqtt.NewBackend(config.C)
	default:
		return fmt.Errorf("unexpected gateway backend type: %s, it must be mqtt", config.C.NetworkServer.Gateway.Backend.Type)
	}

	if err != nil {
		return errors.Wrap(err, "gateway-backend setup failed")
	}

	gwbackend.SetBackend(gw)
	return nil
}

func setupApplicationServer() error {
	if err := applicationserver.Setup(); err != nil {
		return errors.Wrap(err, "application-server setup error")
	}
	return nil
}

func setupJoinServer() error {
	if err := joinserver.Setup(config.C); err != nil {
		return errors.Wrap(err, "setup join-server backend error")
	}
	return nil
}

func setupNetworkController() error {
	if err := controller.SetupNetworkController(config.C); err != nil {
		return errors.Wrap(err, "setup networkController error")
	}
	return nil
}

func setupUplink() error {
	if err := uplink.Setup(config.C); err != nil {
		return errors.Wrap(err, "setup link error")
	}
	return nil
}

func setupDownlink() error {
	if err := downlink.Setup(config.C); err != nil {
		return errors.Wrap(err, "setup downlink error")
	}
	return nil
}

func setupNetworkServerAPI() error {
	if err := ns.Setup(config.C); err != nil {
		return errors.Wrap(err, "setup network-server api error")
	}
	return nil
}

func startLoRaServer(server *uplink.Server) func() error {
	return func() error {
		*server = *uplink.NewServer()
		return server.Start()
	}
}

func setupGateways() error {
	if err := gateway.Setup(config.C); err != nil {
		return errors.Wrap(err, "setup gateway error")
	}
	return nil
}

func startQueueScheduler() error {
	//下行设备队列调度器的分批处理
	log.Info("starting downlink device-queue scheduler")
	go downlink.DeviceQueueSchedulerLoop()

	//多路广播调度器
	log.Info("starting multicast scheduler")
	go downlink.MulticastQueueSchedulerLoop()

	return nil
}
